package net.chrisrichardson.ec2deployer.applications

import groovy.mock.interceptor.MockFor
import net.chrisrichardson.ec2deployer.core.EC2Server
import net.chrisrichardson.ec2deployer.core.AWSProperties
import net.chrisrichardson.ec2deployer.core.S3Synchronizer
import net.chrisrichardson.ec2deployer.cluster.ClusterSpec
import net.chrisrichardson.ec2deployer.core.ProcessExecutionException

class MySqlServerTest extends GroovyTestCase {

    MockFor mockEC2Server
    private MySqlServer mySqlServer
    MockFor mockS3Synchronizer
    def scriptFiles
    def mySqlServerHost

    def mySqlSlaveHost
    private MySqlServer mySqlSlave

    def mockMySqlServer

    def static masterMyCnf = """
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1
log-bin=mysql-bin
server-id=1
lower_case_table_names=1
max_connections=2000
max_allowed_packet=50M

[mysql.server]
user=mysql
basedir=/var/lib

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
"""

    def static masterMyCnfForEbs = """
[mysqld]
datadir=/vol/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1
log-bin=mysql-bin
server-id=1
lower_case_table_names=1
max_connections=2000
max_allowed_packet=50M

[mysql.server]
user=mysql
basedir=/vol/lib

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
"""


    ClusterSpec clusterSpec

    AWSProperties awsProperties

    protected void setUp() {
        super.setUp();
        mockEC2Server = new MockFor(EC2Server.class)
        mockS3Synchronizer = new MockFor(S3Synchronizer.class)
        mockMySqlServer = new MockFor(MySqlServer.class)

        scriptFiles = ["script1.sql", "script2.sql.gz"]

        clusterSpec = new ClusterSpec().schema("foo", ["foouser1": "fooPassword1"], scriptFiles)
        Properties properties = new Properties()
        properties.setProperty("accessKey", "MyAccessKey")
        properties.setProperty("secretKey", "MySecretKey")
        awsProperties = new AWSProperties(properties)
    }

    void testDbCreate() {
        clusterSpec.bucketPrefix "somePrefix"

        mockEC2Server.demand.ssh {createSchemaCommand, script ->
            assertEquals "mysql -u root", createSchemaCommand
            assertEquals """DROP SCHEMA IF EXISTS `foo`;
CREATE SCHEMA `foo`;

system rm -f /var/tmp/dropusers.sql



select concat('DROP USER ',u.user, ';') from mysql.user u where u.user in ('foouser1') into OUTFILE '/var/tmp/dropusers.sql';

source /var/tmp/dropusers.sql


CREATE USER 'foouser1' IDENTIFIED BY 'fooPassword1';

""", script
        }

        mockEC2Server.demand.getAwsProperties {new AWSProperties(new Properties())}

        mockS3Synchronizer.demand.uploadToS3 {files, bucketPrefix, bucket ->
            assertEquals "somePrefix", bucketPrefix
            assertEquals(scriptFiles, files)
            assertEquals "tmp-foo-dml", bucket
        }

        mockS3Synchronizer.demand.downloadFromS3 {host, bucketPrefix, s3Bucket, dir ->
            assertEquals mySqlServerHost, host
            assertEquals "somePrefix", bucketPrefix
            assertEquals "tmp-foo-dml", s3Bucket
            assertEquals "/tmp/tmp-foo-dml", dir
        }

        mockEC2Server.demand.ssh {script1Command ->
            assertEquals "mysql foo < /tmp/tmp-foo-dml/script1.sql", script1Command
        }

        mockEC2Server.demand.ssh {script2Command ->
            assertEquals "gunzip < /tmp/tmp-foo-dml/script2.sql.gz | mysql foo", script2Command
        }

        mockS3Synchronizer.use {
            mockEC2Server.use {
                mySqlServerHost = new EC2Server()
                mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
                mySqlServer.dbCreate()
            }
        }

    }

    public void testConfigureAsMaster() {


        clusterSpec.bucketPrefix "somePrefix"

        mockEC2Server.demand.ssh {stopCmd ->
            assertEquals "/sbin/service mysqld stop", stopCmd
        }

        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'cat > /etc/my.cnf', catCmd
            assertEquals masterMyCnf, file
        }
        mockEC2Server.demand.ssh {startCmd ->
            assertEquals "/sbin/service mysqld start", startCmd
        }

        mockS3Synchronizer.use {
            mockEC2Server.use {
                mySqlServerHost = new EC2Server()
                mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
                mySqlServer.configureAsMaster()
            }
        }
    }
    
    public void testConfigureAsMasterWhenNotRunning() {


        clusterSpec.bucketPrefix "somePrefix"

        mockEC2Server.demand.ssh {stopCmd ->
            assertEquals "/sbin/service mysqld stop", stopCmd
            throw new ProcessExecutionException("My Ssh command", 1)
        }

        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'cat > /etc/my.cnf', catCmd
            assertEquals masterMyCnf, file
        }
        mockEC2Server.demand.ssh {startCmd ->
            assertEquals "/sbin/service mysqld start", startCmd
        }

        mockS3Synchronizer.use {
            mockEC2Server.use {
                mySqlServerHost = new EC2Server()
                mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
                mySqlServer.configureAsMaster()
            }
        }
    }

    public void testConfigureAsSlave() {
        mockMySqlServer.demand.grantAccessToSlave {

        }

        mockEC2Server.demand.ssh {command, text ->

            assertEquals "cat > /etc/my.cnf", command
            assertEquals """
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1
server-id=2
lower_case_table_names=1
max_connections=3000
max_allowed_packet=50M

[mysql.server]
user=mysql
basedir=/var/lib

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
""", text

        }


        mockEC2Server.demand.ssh {command ->
            assertEquals "/sbin/service mysqld restart", command
        }

        mockMySqlServer.demand.getHostPrivateDnsName {
            "fooBar.comm"
        }

        mockEC2Server.demand.ssh {command, text ->
            assertEquals "mysql", command
            assertEquals """
CHANGE MASTER TO MASTER_HOST='fooBar.comm',  MASTER_USER='repl', MASTER_PASSWORD='slavepass', MASTER_LOG_FILE='', MASTER_LOG_POS=4;
start slave;
show slave status\\G
""", text
        }

        mockS3Synchronizer.use {
            mockEC2Server.use {
                    mySqlSlaveHost = new EC2Server()
                    mySqlSlave = new MySqlServer(mySqlSlaveHost, mockMySqlServer.proxyInstance(), clusterSpec)
                    mySqlSlave.configureAsSlave(2)
            }
        }
    }

    public void testDbSave() {
        Properties properties = new Properties()
        properties.setProperty("accessKey", "MyAccessKey")
        properties.setProperty("secretKey", "MySecretKey")
        AWSProperties awsProperties = new AWSProperties(properties)

        mockEC2Server.demand.getAwsProperties {awsProperties}
        mockEC2Server.demand.getAwsProperties {awsProperties}

        mockEC2Server.demand.ssh { String command, String input ->
            assertEquals "rm -f .awssecret ; cat > .awssecret; chmod 0600 .awssecret", command
            assertEquals "MyAccessKey MySecretKey", input
        }

        mockEC2Server.demand.ssh { String command ->
            assertEquals "aws mkdir myBucket", command
        }
        
        mockEC2Server.demand.ssh { String command ->
            assertEquals "mysqldump foo  > /var/tmp/mysqldump.sql", command
        }

        mockEC2Server.demand.ssh { String command ->
            assertEquals "s3put myBucket/myObject /var/tmp/mysqldump.sql", command
        }

        mockEC2Server.use {
            mySqlServerHost = new EC2Server()
            mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
            mySqlServer.dbSave("myBucket/myObject")
        }

    }
    public void testDbRestore() {
        Properties properties = new Properties()
        properties.setProperty("accessKey", "MyAccessKey")
        properties.setProperty("secretKey", "MySecretKey")
        AWSProperties awsProperties = new AWSProperties(properties)

        mockEC2Server.demand.getAwsProperties {awsProperties}
        mockEC2Server.demand.getAwsProperties {awsProperties}

        mockEC2Server.demand.ssh { String command, String input ->
            assertEquals "rm -f .awssecret ; cat > .awssecret; chmod 0600 .awssecret", command
            assertEquals "MyAccessKey MySecretKey", input
        }

        mockEC2Server.demand.ssh { String command ->
            assertEquals "s3get myBucket/myObject /var/tmp/mysqldump.sql", command
        }

        mockEC2Server.demand.ssh { String command ->
            assertEquals "mysql foo  < /var/tmp/mysqldump.sql", command
        }

        mockEC2Server.use {
            mySqlServerHost = new EC2Server()
            mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
            mySqlServer.dbRestore("myBucket/myObject")
        }

    }

    public void testConfigureBackup_master() {
        configureExpectationsForBackup()
        mockEC2Server.demand.ssh { command, input ->
            assertEquals "cat > /etc/cron.d/mysql2s3", command
            assertEquals """10 2 * * * root /root/mysql2s3
""", input
        }
        mockEC2Server.use {
            mySqlServerHost = new EC2Server()
            mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
            mySqlServer.configureBackup "myBucket", "myObject_", true
        }

    }

    private void configureExpectationsForBackup() {
        mockEC2Server.demand.getAwsProperties {awsProperties}
        mockEC2Server.demand.getAwsProperties {awsProperties}
        mockEC2Server.demand.ssh { String command, String input ->
            assertEquals "rm -f .awssecret ; cat > .awssecret; chmod 0600 .awssecret", command
            assertEquals "MyAccessKey MySecretKey", input
        }

        mockEC2Server.demand.ssh {command, input ->
            assertEquals "cat > /root/mysql2s3", command
            assertEquals """date >> /var/log/mysql2s3
mysqldump --add-drop-database  --databases foo | gzip >  /var/tmp/mysql2s3/mysql2s3.sql.gz
now=`date +%d%m%y_%H%M`
aws put myBucket/myObject__\${now}.sql.gz  /var/tmp/mysql2s3/mysql2s3.sql.gz
aws copy  myBucket/slave_latest myBucket/myObject__\${now}.sql.gz
""", input
        }
        mockEC2Server.demand.ssh {command ->
            assertEquals "chmod u+x /root/mysql2s3", command
        }
        mockEC2Server.demand.ssh {command ->
            assertEquals "mkdir /var/tmp/mysql2s3", command
        }
        mockEC2Server.demand.ssh {command ->
            assertEquals "chmod og-xrw /var/tmp/mysql2s3", command
        }
    }

    public void testConfigureBackup_slave() {
        configureExpectationsForBackup()
        mockEC2Server.demand.ssh { command, input ->
            assertEquals "cat > /etc/cron.d/mysql2s3", command
            assertEquals """*/10 * * * * root /root/mysql2s3
""", input
        }
        mockEC2Server.use {
            mySqlServerHost = new EC2Server()
            def master = new MySqlServer(mySqlServerHost, clusterSpec)
            mySqlServer = new MySqlServer(mySqlServerHost, master, clusterSpec)
            mySqlServer.configureBackup "myBucket", "myObject_", false
        }

    }

    public void testExecuteSqlScriptsFromS3() {
        Properties properties = new Properties()
        properties.setProperty("accessKey", "MyAccessKey")
        properties.setProperty("secretKey", "MySecretKey")
        AWSProperties awsProperties = new AWSProperties(properties)

        mockEC2Server.demand.getAwsProperties {awsProperties}

        mockEC2Server.demand.ssh {cmd, input ->
            assertEquals "cat > .awssecret", cmd
            assertEquals "MyAccessKey MySecretKey", input
        }

        mockEC2Server.demand.ssh {cmd ->
            assertEquals "s3get somebucket/someobject.sql /var/tmp/foo.sql", cmd
        }

        mockEC2Server.demand.ssh {cmd ->
            assertEquals "mysql foo < /var/tmp/foo.sql", cmd
        }

        mockEC2Server.use {
            mySqlServerHost = new EC2Server()
            mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
            mySqlServer.executeSqlScripts(["s3://somebucket/someobject.sql"])
        }

    }


    public void testConfigureAsMasterWithEbsAndInitialization() {

        clusterSpec.withNewEbsVolume(10, "/dev/sdq")

        clusterSpec.bucketPrefix "somePrefix"

        mockEC2Server.demand.ssh {restartCmd ->
            assertEquals "/sbin/service mysqld stop", restartCmd
        }

        mockEC2Server.demand.createEbsVolume { ebsVolume ->
            assert clusterSpec.ebsVolume, ebsVolume
            ebsVolume.volumeId = "vol-1234"
        }

        mockEC2Server.demand.attachEbsVolume { ebsVolume ->
            assert clusterSpec.ebsVolume, ebsVolume
        }

        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'dos2unix|bash', catCmd
            assertEquals """yum -y install xfsprogs


mkfs.xfs /dev/sdq


echo "/dev/sdq /vol xfs noatime 0 0" >> /etc/fstab

mkdir /vol
mount /vol


mkdir /vol/lib /vol/log
mv /var/lib/mysql /vol/lib/

""", file
        }
        
        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'cat > /etc/my.cnf', catCmd
            assertEquals masterMyCnfForEbs, file
        }
        mockEC2Server.demand.ssh {restartCmd ->
            assertEquals "/sbin/service mysqld start", restartCmd
        }
 
        mockS3Synchronizer.use {
            mockEC2Server.use {
                mySqlServerHost = new EC2Server()
                mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
                mySqlServer.configureAsMaster()
            }
        }
    }
    public void testConfigureAsMasterWithExistingEbsVolume() {

        clusterSpec.withExistingEbsVolume("vol-123434", "/dev/sdq")

        clusterSpec.bucketPrefix "somePrefix"

        mockEC2Server.demand.ssh {restartCmd ->
            assertEquals "/sbin/service mysqld stop", restartCmd
        }

        mockEC2Server.demand.attachEbsVolume { ebsVolume ->
            assert clusterSpec.ebsVolume, ebsVolume
        }

        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'dos2unix|bash', catCmd
            assertEquals """yum -y install xfsprogs



echo "/dev/sdq /vol xfs noatime 0 0" >> /etc/fstab

mkdir /vol
mount /vol


""", file
        }

        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'cat > /etc/my.cnf', catCmd
            assertEquals masterMyCnfForEbs, file
        }
        mockEC2Server.demand.ssh {restartCmd ->
            assertEquals "/sbin/service mysqld start", restartCmd
        }
        
        mockS3Synchronizer.use {
            mockEC2Server.use {
                mySqlServerHost = new EC2Server()
                mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
                mySqlServer.configureAsMaster()
            }
        }
    }

    public void testConfigureAsMasterWitEbsVolumeFromSnapshot() {

        clusterSpec.withEbsVolumeFromSnapshot("snap-123434", 10, "/dev/sdq")

        clusterSpec.bucketPrefix "somePrefix"

        mockEC2Server.demand.ssh {restartCmd ->
            assertEquals "/sbin/service mysqld stop", restartCmd
        }

        mockEC2Server.demand.createEbsVolume { ebsVolume ->
            assert clusterSpec.ebsVolume, ebsVolume
            ebsVolume.volumeId = "vol-1234"
        }
        
        mockEC2Server.demand.attachEbsVolume { ebsVolume ->
            assert clusterSpec.ebsVolume, ebsVolume
        }

        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'dos2unix|bash', catCmd
            assertEquals """yum -y install xfsprogs



echo "/dev/sdq /vol xfs noatime 0 0" >> /etc/fstab

mkdir /vol
mount /vol


""", file
        }

        mockEC2Server.demand.ssh {catCmd, file ->
            assertEquals 'cat > /etc/my.cnf', catCmd
            assertEquals masterMyCnfForEbs, file
        }
        mockEC2Server.demand.ssh {restartCmd ->
            assertEquals "/sbin/service mysqld start", restartCmd
        }

        mockS3Synchronizer.use {
            mockEC2Server.use {
                mySqlServerHost = new EC2Server()
                mySqlServer = new MySqlServer(mySqlServerHost, clusterSpec)
                mySqlServer.configureAsMaster()
            }
        }
    }

}